package web

import (
	"embed"
	"gitee.com/go-boot/boot-interfaces/ihttp"
	"net/http"
	"path"
	"strings"
)

type (
	HttpRouterParam struct {
		key   string
		value string
	}

	HttpRouterParams struct {
		params []HttpRouterParam
	}

	// HttpRouter 路由
	HttpRouter struct {
		path            string
		fullPath        string
		children        []ihttp.IRouter
		parent          ihttp.IRouter
		param           HttpRouterParam
		methodHandler   map[string]ihttp.HandlerFunc
		notfoundHandler ihttp.HandlerFunc
	}
)

func NewRouter(basePath string) *HttpRouter {
	var (
		path  string
		param = HttpRouterParam{}
	)
	path = basePath
	if strings.HasPrefix(basePath, "/") {
		path = basePath[1:]
	}
	if strings.Contains(path, "/") {
		panic("不支持多级路由")
	}
	if strings.HasPrefix(path, ":") {
		param.key = path
	}
	return &HttpRouter{
		path:          basePath,
		fullPath:      "/" + path,
		children:      make([]ihttp.IRouter, 0),
		parent:        nil,
		param:         param,
		methodHandler: make(map[string]ihttp.HandlerFunc, 6),
	}
}

func NewStaticRouter(urlPath, filePath string, embed embed.FS) *HttpRouter {
	var fileServer = http.FileServer(http.FS(embed))
	handler := func(ctx ihttp.IHttpContext) {
		if ctx.Request().Path() == urlPath {
			ctx.Request().SetPath(filePath + "/")
		} else {
			ctx.Request().SetPath(path.Join(filePath, ctx.Request().Path()))
		}
		fileServer.ServeHTTP(ctx.Response().Response(), ctx.Request().Request())
	}
	m := make(map[string]ihttp.HandlerFunc, 2)
	m[http.MethodGet] = handler
	m[http.MethodHead] = handler
	return &HttpRouter{
		path:          path.Join(urlPath, "/*static"),
		fullPath:      urlPath,
		children:      make([]ihttp.IRouter, 0),
		parent:        nil,
		methodHandler: m,
	}
}

func (h *HttpRouterParam) Key() string {
	return h.key
}

func (h *HttpRouterParam) Value() string {
	return h.value
}

func (h *HttpRouterParam) Set(key string, value string) ihttp.IRouterParam {
	h.key = key
	h.value = value
	return h
}

func (h *HttpRouterParams) Get(name string) (value string, exist bool) {
	for _, entry := range h.params {
		if entry.Key() == name {
			return entry.Value(), true
		}
	}
	return "", false
}

func (h *HttpRouterParams) Set(key string, value string) {
	for i := 0; i < len(h.params); i++ {
		if h.params[i].Key() == key {
			h.params[i].Set(key, value)
			return
		}
	}
	h.params = append(h.params, HttpRouterParam{key: key, value: value})
}

func (r *HttpRouter) Parent(parent ihttp.IRouter) ihttp.IRouter {
	r.parent = parent
	return r
}

func (r *HttpRouter) Push(routers ...ihttp.IRouter) ihttp.IRouter {
	for i := 0; i < len(routers); i++ {
		routers[i].Parent(r)
	}
	r.children = append(r.children, routers...)
	return r
}

func (r *HttpRouter) Router(method string, handler ihttp.HandlerFunc) ihttp.IRouter {
	r.methodHandler[method] = handler
	return r
}

func (r *HttpRouter) Head(handler ihttp.HandlerFunc) ihttp.IRouter {
	return r.Router(http.MethodHead, handler)
}

func (r *HttpRouter) Get(handler ihttp.HandlerFunc) ihttp.IRouter {
	return r.Router(http.MethodGet, handler)
}

func (r *HttpRouter) Post(handler ihttp.HandlerFunc) ihttp.IRouter {
	return r.Router(http.MethodPost, handler)
}

func (r *HttpRouter) Delete(handler ihttp.HandlerFunc) ihttp.IRouter {
	return r.Router(http.MethodDelete, handler)
}

func (r *HttpRouter) Put(handler ihttp.HandlerFunc) ihttp.IRouter {
	return r.Router(http.MethodPut, handler)
}

func (r *HttpRouter) Patch(handler ihttp.HandlerFunc) ihttp.IRouter {
	return r.Router(http.MethodPatch, handler)
}

func (r *HttpRouter) Options(handler ihttp.HandlerFunc) ihttp.IRouter {
	return r.Router(http.MethodOptions, handler)
}

func (r *HttpRouter) Any(handler ihttp.HandlerFunc) ihttp.IRouter {
	r.Router(http.MethodHead, handler)
	r.Router(http.MethodGet, handler)
	r.Router(http.MethodPost, handler)
	r.Router(http.MethodDelete, handler)
	r.Router(http.MethodPut, handler)
	r.Router(http.MethodPatch, handler)
	r.Router(http.MethodOptions, handler)
	return r
}

func (r *HttpRouter) Handler(method string) (handler ihttp.HandlerFunc, exist bool) {
	handler, exist = r.methodHandler[method]
	return
}

func (r *HttpRouter) Children() []ihttp.IRouter {
	return r.children
}

func (r *HttpRouter) Path() string {
	return r.path
}

func (r *HttpRouter) Methods() []string {
	var methods []string
	for s := range r.methodHandler {
		methods = append(methods, s)
	}
	return methods
}

func (r *HttpRouter) IsEnd() bool {
	return len(r.children) == 0
}
